
#ifndef _ARMATURE_H
#define _ARMATURE_H

//---------------------------------------------------------------------------
//  Includes
//---------------------------------------------------------------------------

#include <string>
#include <cstdio>
#include <map>

#include <defEException.h>
#include <defESphere.h>
#include <defEVectorMath.h>
#include <defEGeometry.h>
#include <defEGeomUtils.h>
#include <defERangeVector.h>

#include "HandConfig.h"

namespace vhand
{

//---------------------------------------------------------------------------
//  Defines
//---------------------------------------------------------------------------

using defE::XError;
using defE::IndexRange;
using defE::IndexRanges;
using defE::Sphere;
using defE::Point3;
using defE::Vector3;
using defE::Vector4;
using defE::Matrix4;
using defE::Quat;
using defE::CoordSys;

/** The number of limbs that can move up-down
 */
static const int NUMFLEX = 17; 

/** The number of limbs that can move left-right
 */
static const int NUMABDUCT = 7;

/** The enum of all limbs of the hand.
 * The first NUMABDUCT limbs in the enum can move left-right.
 * All NUMFLEX limbs in the enum can move up-down.
 * 'r' points out that this is the right hand.
 */
typedef enum{
    rForearm, rHand,     
    rThumb1, rIndex1, rMid1, rRing1, rPinky1, 
    rThumb2, rIndex2, rMid2, rRing2, rPinky2,
    rThumb3, rIndex3, rMid3, rRing3, rPinky3
} LimbE;


//***************************************************************************
//                              LimbRef
//***************************************************************************

/**
 */

class LimbRef
{
public:
    LimbRef();
    LimbRef( const LimbRef &source );
    ~LimbRef();
    LimbRef& operator = ( const LimbRef &source );

    int getID( void ) const { return _id; }
    void setID( unsigned int id ) { _id = id; }

    std::string getName( void ) const { return _name; }
    void setName( std::string name ) { _name = name; }

#if 0
    unsigned int getBonesOffset( void ) const { return _bones_offset; }
    void setBonesOffset( unsigned int ofs ) { _bones_offset = ofs; }
    unsigned int getBonesSize( void ) const { return _bones_size; }
    void setBonesSize( unsigned int size ) { _bones_size = size; }

    unsigned int getSkinOffset( void ) const { return _skin_offset; }
    void setSkinOffset( unsigned int ofs ) { _skin_offset = ofs; }
    unsigned int getSkinSize( void ) const { return _skin_size; }
    void setSkinSize( unsigned int size ) { _skin_size = size; }

    unsigned int getSpheresOffset( void ) const { return _spheres_offset; }
    void setSpheresOffset( unsigned int ofs ) { _spheres_offset = ofs; }
    unsigned int getSpheresSize( void ) const { return _spheres_size; }
    void setSpheresSize( unsigned int size ) { _spheres_size = size; }
#endif

    const IndexRanges* getBonesRef() const { return &_bonesRef; }
    IndexRanges* editBonesRef() { return &_bonesRef; }
    
    const IndexRanges* getSkinRef() const { return &_skinRef; }
    IndexRanges* editSkinRef() { return &_skinRef; }

    const IndexRanges* getSpheresRef() const { return &_spheresRef; }
    IndexRanges* editSpheresRef() { return &_spheresRef; }

    Matrix4 getTransformation( void ) const { return _trlmat * _rotmat; }
    Matrix4 getRotation( void ) const { return _rotmat; }
    Matrix4 getTranslation( void ) const { return _trlmat; }
    
    void setRotation( const Quat &quat );
    void setRotation( const Matrix4 &rotmat );
    void setTranslation( const Vector3 &trlvec );
    void rotate( const Matrix4 &rotmatnew );

    const std::vector< int >* getChildrenIDs( void ) const { return &_id_children; }
    std::vector< int >* editChildrenIDs( void ) { return &_id_children; }

    int getParentID( void ) const { return _id_parent; }
    void setParentID( int pa_id ) { _id_parent = pa_id; }

    unsigned int getNumChildIDs( void ) const { return _id_children.size(); }
    void addChildID( int id ) { _id_children.push_back( id ); }

    /**
     * @bug
     *      root id = -1 or root id = 0 ?
     */
    //bool isRoot() const { return _id_parent == -1; }
    bool isLeaf() const { return _id_children.size() == 0; }

    void print( FILE *file = stdout ) const;

private:

    /** The identifier of this limb
     */
    int _id;
    std::string _name;


#if 0
    unsigned int _bones_offset;
    unsigned int _bones_size;
    unsigned int _skin_offset;
    unsigned int _skin_size;
    unsigned int _spheres_offset;
    unsigned int _spheres_size;
#endif

    /** Refer to the locations of the vertices attached to this limb anchor
     */
    IndexRanges _bonesRef;
    IndexRanges _skinRef;
    IndexRanges _spheresRef;

    /** The transformation matrix of the current limb according to the parent
     * limb
     */
    Matrix4 _rotmat;
    Matrix4 _trlmat;

    /** The immediately adjacent child limbs of the current limb
     */
    std::vector< int > _id_children;

    /** The immediately parent limb
     */
    int _id_parent;

}; // class LimbRef

//***************************************************************************
//                              Armature
//***************************************************************************

/**
 */
class Armature
{
public:
    Armature();

    int findLimbID( std::string name );
    std::string findLimbName( int id );

    unsigned int getLimbNum() const { return _num_limbs; }
    void setLimbNum( unsigned int nl ) { _num_limbs = nl; }

    const std::vector<LimbRef>* getAllLimbRef() const { return &_alllimbrefs; }
    std::vector<LimbRef>* editAllLimbRef() { return &_alllimbrefs; }

    Matrix4 getTransformationToRoot( LimbRef limb ) const;
    void updateRange( int id );
    void moveLimb( std::string, const Matrix4 &limbmat );
    void moveLimb( int limbid, const Matrix4 &limbmat );

    Matrix4 getUpdateMatrix( void ) const { return _update_mat; }

    const IndexRanges* getBonesUpdateRange( void ) const { return &_bones_update_range; }
    IndexRanges* editBonesUpdateRange( void ) { return &_bones_update_range; }
    const IndexRanges* getSkinUpdateRange( void ) const { return &_skin_update_range; }
    IndexRanges* editSkinUpdateRange( void ) { return &_skin_update_range; }
    const IndexRanges* getSpheresUpdateRange( void ) const { return &_spheres_update_range; }
    IndexRanges* editSpheresUpdateRange( void ) { return &_spheres_update_range; }

    const std::map<int,int>* getBonesToLimb( void ) const { return &_bones2limb; }
    std::map<int,int>* editBonesToLimb( void ) { return &_bones2limb; }
    const std::map<int,int>* getSkinToLimb( void ) const { return &_skin2limb; }
    std::map<int,int>* editSkinToLimb( void ) { return &_skin2limb; }
    const std::map<int,int>* getSpheresToLimb( void ) const { return &_spheres2limb; }
    std::map<int,int>* editSpheresToLimb( void ) { return &_spheres2limb; }

    void makeLimbBonesRef( void );
    void makeLimbSkinRef( void );
    void makeLimbSpheresRef( void );

    int initGraphicsResource( void );
    int draw( void ) const;

private:

    friend class Loader;

    /** The number of limbs in the armature
     */
    unsigned int _num_limbs;

    /** The array of all limbrefs of the armature in the acutural state
     */
    std::vector< LimbRef > _alllimbrefs;

    /** The relation between the bone ID ans Name
     */
    std::map< std::string, int > _limb_str2id;
    std::map< int, std::string > _limb_id2str;

    /** The map objects, which save the temporary information about the
     * vertices and LimbRef, and help to obtain the location references to
     * bones/skin/spheres in each LimbRef object.
     */
    std::map< int, int > _bones2limb;
    std::map< int, int > _skin2limb;
    std::map< int, int > _spheres2limb;

    /** The finally calculated matrix to update the coordinates of moved
     * vertices
     */
    Matrix4 _update_mat;

    /** Point out the set of moved vertices
     */
    IndexRanges _bones_update_range;
    IndexRanges _skin_update_range;
    IndexRanges _spheres_update_range;

    /**
     */
    CoordSys _geom_coordsys;

}; // class Armature

} // namespace vhand

#endif // _ARMATURE_H
